import java.awt.*;
import java.awt.event.*;
import java.io.*;
import ij.plugin.frame.*;
import ij.*;
import ij.process.*;
import ij.gui.*;

/**
	Image Processing Demo. Demonstrates how to create a custom user interface, how
	to use ImageJ's ImageProcessor class, and how to run commands in a separate thread.
*/
public class IP_Demo extends PlugInFrame implements ActionListener {

	Panel panel;
	int previousID;
	static Frame instance;

	public IP_Demo() {
		super("IP Demo");
		if (IJ.versionLessThan("1.39t"))
			return;
		if (instance!=null) {
			instance.toFront();
			return;
		}
		instance = this;
		addKeyListener(IJ.getInstance());

		setLayout(new FlowLayout());
		panel = new Panel();
		panel.setLayout(new GridLayout(4, 4, 5, 5));
		addButton("Reset");
		addButton("Flip");
		addButton("Invert");
		addButton("Rotate");
		addButton("Lighten");
		addButton("Darken");
		addButton("Zoom In");
		addButton("Zoom Out");
		addButton("Smooth");
		addButton("Sharpen");
		addButton("Find Edges");
		addButton("Threshold");
		addButton("Add Noise");
		addButton("Reduce Noise");
		addButton("Macro1");
		addButton("Macro2");
		add(panel);
		
		pack();
		GUI.center(this);
		show();
	}
	
	void addButton(String label) {
		Button b = new Button(label);
		b.addActionListener(this);
		b.addKeyListener(IJ.getInstance());
		panel.add(b);
	}

	public void actionPerformed(ActionEvent e) {
		ImagePlus imp = WindowManager.getCurrentImage();
		if (imp==null) {
			IJ.beep();
			IJ.showStatus("No image");
			previousID = 0;
			return;
		}
		if (!imp.lock())
			{previousID = 0; return;}
		ImageProcessor ip = imp.getProcessor();
		int id = imp.getID();
		if (id!=previousID)
			ip.snapshot();
		previousID = id;
		String label = e.getActionCommand();
		if (label==null)
			return;
		new IPDemoRunner(label, imp, ip);
	}

	public void processWindowEvent(WindowEvent e) {
		super.processWindowEvent(e);
		if (e.getID()==WindowEvent.WINDOW_CLOSING) {
			instance = null;	
		}
	}

}

class IPDemoRunner extends Thread {
	private String command;
	private ImagePlus imp;
	private ImageProcessor ip;

	IPDemoRunner(String command, ImagePlus imp, ImageProcessor ip) {
		super(command);
		this.command = command;
		this.imp = imp;
		this.ip = ip;
		setPriority(Math.max(getPriority()-2, MIN_PRIORITY));
		start();
	}

	public void run() {
		try {runCommand(command, imp, ip);}
		catch(OutOfMemoryError e) {
			IJ.outOfMemory(command);
			if (imp!=null) imp.unlock();
		} catch(Exception e) {
			CharArrayWriter caw = new CharArrayWriter();
			PrintWriter pw = new PrintWriter(caw);
			e.printStackTrace(pw);
			IJ.write(caw.toString());
			IJ.showStatus("");
			if (imp!=null) imp.unlock();
		}
	}

	void runCommand(String command, ImagePlus imp, ImageProcessor ip) {
		IJ.showStatus(command + "...");
		Roi roi = imp.getRoi();
		if (command.startsWith("Zoom")||command.startsWith("Macro")||command.equals("Threshold"))
			{roi = null; ip.resetRoi();}
		ImageProcessor mask =  roi!=null?roi.getMask():null;
		if (command.equals("Reset"))
			ip.reset();
		else if (command.equals("Flip"))
			ip.flipVertical();
		else if (command.equals("Invert"))
			ip.invert();
		else if (command.equals("Lighten")) {
			if (imp.isInvertedLut())
				ip.multiply(0.9);
			else
				ip.multiply(1.1);
		}
		else if (command.equals("Darken")) {
			if (imp.isInvertedLut())
				ip.multiply(1.1);
			else
				ip.multiply(0.9);
		}
		else if (command.equals("Rotate"))
			ip.rotate(30);
		else if (command.equals("Zoom In"))
			ip.scale(1.2, 1.2);
		else if (command.equals("Zoom Out"))
			ip.scale(.8, .8);
		else if (command.equals("Threshold"))
			ip.autoThreshold();
		else if (command.equals("Smooth"))
			ip.smooth();
		else if (command.equals("Sharpen"))
			ip.sharpen();
		else if (command.equals("Find Edges"))
			ip.findEdges();
		else if (command.equals("Add Noise"))
			ip.noise(20);
		else if (command.equals("Reduce Noise"))
			ip.medianFilter();
		else if (command.equals("Macro1"))
			macro1(imp, ip);
		else if (command.equals("Macro2"))
			macro2(imp, ip);
		if (mask!=null) ip.reset(mask);
		imp.updateAndDraw();
		imp.unlock();
		IJ.showStatus("");
	}

	void macro1(ImagePlus imp, ImageProcessor ip) {
		IJ.resetEscape();
		for (int i=10; i<=360; i+=10) {
			ip.reset();
			ip.rotate(i);
			imp.updateAndDraw();
			if (!imp.isVisible() || IJ.escapePressed()) return;
			IJ.wait(10);
		}
	}
	
	void macro2(ImagePlus imp, ImageProcessor ip) {
		IJ.resetEscape();
		double scale = 1, m = 1.2;
		for (int i=0; i<20; i++) {
			ip.reset();
			scale *= m;
			ip.scale(scale, scale);
			imp.updateAndDraw();
			if (!imp.isVisible() || IJ.escapePressed()) return;
			IJ.wait(10);
		}
		for (int i=0; i <20; i++) {
			ip.reset();
			scale /= m;
			ip.scale(scale, scale);
			imp.updateAndDraw();
			if (!imp.isVisible() || IJ.escapePressed()) return;
			IJ.wait(10);
		}
	}

}
